home *** CD-ROM | disk | FTP | other *** search
- /* 4567890123456789012345678901234567890123456789012345678901234567 */
- #include <Dialogs.h>
- #include <Folders.h>
- #include <Script.h>
- #include <Resources.h>
- #include <TextUtils.h>
-
- #include "EvenMoreFiles.h"
- #include "DSDocumentList.h"
- #include "ResourceDefinitions.h"
- #include "Toolbox.h"
-
- #include "SaveSafely.h"
-
- #define kKilobytesPerByte (1024)
-
- static OSErr CopyUnknownResTypes(short aSourceResFile,
- short aDestinationResFile, ResType *aTypeList, short aTypeCount);
- static OSErr GetTemporaryName(Str255 theTemporaryName);
- static Boolean InTypeList(ResType aType, short aTypeCount,
- ResType *aList);
- static OSErr GetTrashSpace(short aVRefNum,
- unsigned long *aTrashSpace);
-
- OSErr DSGetFileSize(unsigned long *aFileSize);
-
- #if STRICT_WINDOWS
- OSErr SaveSafely(WindowRef aWindow, ResType *aTypeList,
- short aTypeCount, WriteDataProcPtr aWriteDFProc,
- WriteDataProcPtr aWriteRFProc) {
- #else
- OSErr SaveSafely(WindowPtr aWindow, ResType *aTypeList,
- short aTypeCount, WriteDataProcPtr aWriteDFProc,
- WriteDataProcPtr aWriteRFProc) {
- #endif
- OSErr theError = noErr;
- unsigned long theFileSize, theSpaceAvailable, theTrashSpace;
- FSSpec theOriginalFile, theTemporaryFile;
- short theTemporaryDFRefNum, theTemporaryRFRefNum;
- short theOriginalDFRefNum, theOriginalRFRefNum;
- short theTemporaryItemsVRefNum;
- long theTemporaryItemsDirID, theTemporaryDirID;
- Str255 theTemporaryName;
- OSType theTemporaryCreator, theTemporaryFileType;
-
- /*
- * Get the file reference number of the data fork of the file
- * currently associated with the window to save.
- */
- theError = DSGetWindowDFRefNum(aWindow, &theOriginalDFRefNum);
- if (theError == noErr) {
- /*
- * Get the file reference number of the resource fork of the file
- * currently associated with the window to save.
- */
- theError = DSGetWindowRFRefNum(aWindow, &theOriginalRFRefNum);
- }
- if (theError == noErr) {
- /*
- * Get the FSSpec for the original file from the reference number.
- */
- theError = GetFileSpec(theOriginalDFRefNum, &theOriginalFile);
- }
- if (theError == noErr) {
- /*
- * Determine the final size of the new file.
- */
- theError = DSGetFileSize(&theFileSize);
- }
- if (theError == noErr) {
- /*
- * Determine the amount of free space on the volume.
- */
- theError = GetFreeBytesOnVolume(theOriginalFile.vRefNum,
- &theSpaceAvailable);
- }
- if (theError == noErr) {
- if (theFileSize > theSpaceAvailable) {
- /*
- * There is insufficient free space on the volume to do the safe-save.
- *
- * See if there is anything in the Trash on this volume.
- */
- theError = GetTrashSpace(theOriginalFile.vRefNum,
- &theTrashSpace);
- if (theError == noErr) {
- if (theFileSize >
- (theSpaceAvailable + theTrashSpace)) {
- /*
- * Not enough space even if we empty Trash.
- */
- (void)StopAlert(rNotEnoughSpaceAlert, nil);
- } else {
- Str255 theTrashSpaceString;
- Str255 theVolumeName;
- /*
- * If the user emptied the Trash, we would have enough space.
- */
- NumToString(theTrashSpace / kKilobytesPerByte,
- theTrashSpaceString);
- theError = GetVolumeName(theOriginalFile.vRefNum,
- theVolumeName);
- if (theError == noErr) {
- ParamText(theVolumeName, theTrashSpaceString,
- kNullString, kNullString);
- } else {
- ParamText("\pthe volume", theTrashSpaceString,
- kNullString, kNullString);
- }
- (void)StopAlert(rEmptyTrashAlert, nil);
- }
- /*
- * In either case, the user needs to take further action before we can
- * save. Notify the caller that the user cancelled so that we do not
- * mark the document clean.
- */
- theError = userCanceledErr;
- }
- } else {
- /*
- * There is enough free space on the volume to do the safe-save.
- *
- * We need to create a temporary file. We would like to put it in
- * the Temporary Items folder but it may not be available. See if it
- * is.
- */
- theError = FindFolder(theOriginalFile.vRefNum,
- kTemporaryFolderType, kCreateFolder,
- &theTemporaryItemsVRefNum, &theTemporaryItemsDirID);
- if (theError == dupFNErr) {
- /*
- * This means that the Temporary Items folder IS supported but there
- * is a file by that name where the folder should be. Wierd!
- *
- * We will create the temporary file in the same folder as the
- * original file.
- */
- theTemporaryDirID = theOriginalFile.parID;
- theError = noErr;
- } else if (theError == fnfErr) {
- /*
- * The volume does not support the Temporary Items folder (for some
- * reason or other).
- *
- * We will create the temporary file in the same folder as the
- * original file.
- */
- theTemporaryDirID = theOriginalFile.parID;
- theError = noErr;
- } else if (theError == noErr) {
- /*
- * The volume does support the Temporary Items folder so we will
- * create the temporary file there.
- */
- theTemporaryDirID = theTemporaryItemsDirID;
- } else {
- /*
- * Some other error occurred. It could be paramErr (-50) which would
- * indicate a programming error, either here (heaver forbid) or in the
- * caller. In any case we just return the error.
- */
- theTemporaryDirID = 0; /* to prevent unwanted warnings */
- }
- }
- if (theError == noErr) {
- /*
- * We are about to create the temporary file, but first we have to
- * give it a name.
- */
- theError = GetTemporaryName(theTemporaryName);
- }
- if (theError == noErr) {
- /*
- * Make an FSSpec out of what we have so far.
- */
- theError = FSMakeFSSpec(theOriginalFile.vRefNum,
- theTemporaryDirID, theTemporaryName,
- &theTemporaryFile);
- /*
- * If this worked, the error will be file not found.
- */
- if (theError == fnfErr) {
- /*
- * Get the creator for the temporary file (and the file type). This
- * should be the same as the file we are replacing. This way, if
- * something should go wrong, the user may be able to salvage part of
- * the file.
- */
- theError = DSGetCreatorAndFileType(aWindow,
- &theTemporaryCreator, &theTemporaryFileType);
- } else {
- /*
- * This really ought to be a do {} while (theError != fnfErr);
- */
- DebugStr("\pDuplicate Temporary File Name!");
- }
- }
- if (theError == noErr) {
- /*
- * Go ahead and create the temporary file.
- */
- theError = FSpCreate(&theTemporaryFile,
- theTemporaryCreator, theTemporaryFileType,
- smSystemScript); /* smCurrentScript might be
- better */
- }
- if (theError == noErr) {
- /*
- * Create a resource fork in the file, too. It might end up being
- * empty, but we do not really care.
- */
- FSpCreateResFile(&theTemporaryFile,
- theTemporaryCreator, theTemporaryFileType,
- smSystemScript); /* smCurrentScript might be
- better */
- theError = ResError();
- }
- if (theError == noErr) {
- /*
- * Open the data fork of the file we just created.
- */
- theError = FSpOpenDF(&theTemporaryFile,
- fsRdWrPerm, &theTemporaryDFRefNum);
- }
- if (theError == noErr) {
- /*
- * Open the resource fork of the file we just created.
- */
- theTemporaryRFRefNum = FSpOpenResFile(&theTemporaryFile,
- fsRdWrPerm);
- theError = ResError();
- }
- if (theError == noErr) {
- /*
- * We will call-back to the caller to actually save the information to
- * the file, but there may be things in the resource fork of the file
- * that the application is not aware of ('ckid', 'MPSR', etc.). First
- * we copy all of the resources in the resource fork of the original
- * file that the application does not say that it will write into our
- * temporary file.
- */
- theError = CopyUnknownResTypes(theOriginalRFRefNum,
- theTemporaryRFRefNum, aTypeList, aTypeCount);
- }
- if (theError == noErr) {
- /*
- * Write the data fork data.
- */
- if (aWriteDFProc != nil) {
- theError = aWriteDFProc(aWindow,
- theTemporaryDFRefNum);
- }
- }
- if (theError == noErr) {
- /*
- * Write the resource fork data.
- */
- if (aWriteRFProc != nil) {
- theError = aWriteRFProc(aWindow,
- theTemporaryRFRefNum);
- }
- }
- if (theError == noErr) {
- /*
- * Exchange the file control blocks for the two files
- */
- theError = FSpExchangeFiles(&theOriginalFile,
- &theTemporaryFile);
- }
- if (theError == noErr) {
- /*
- * Close the data fork of the file in the Temporary Items folder.
- */
- if (theOriginalDFRefNum != 0) {
- theError = FSClose(theOriginalDFRefNum);
- }
- }
- if (theError == noErr) {
- /*
- * Close the resource fork of the file in the Temporary Items folder.
- */
- if (theOriginalRFRefNum != 0) {
- CloseResFile(theOriginalRFRefNum);
- theError = ResError();
- }
- }
- if (theError == noErr) {
- /*
- * At this point, the file reference number of the original file
- * points to the file control block of the temporary file (this is
- * a good thing).
- *
- * Delete the file that is in the Temporary Items folder (which is
- * really the original file).
- */
- theError = FSpDelete(&theTemporaryFile);
- }
- if (theError == noErr) {
- /*
- * Tell the application to use the data fork reference number of the
- * temporary file (which contains the new data).
- */
- theError = DSSetWindowDFRefNum(aWindow,
- theTemporaryDFRefNum);
- }
- if (theError == noErr) {
- /*
- * Tell the application to use the resource fork reference number of
- * the temporary file (which contains the new data).
- */
- theError = DSSetWindowRFRefNum(aWindow,
- theTemporaryRFRefNum);
- }
- }
- return theError;
- }
-
- OSErr CopyUnknownResTypes(short aSourceResFile,
- short aDestinationResFile, ResType *aTypeList,
- short aTypeCount) {
- OSErr theError = noErr;
- short theSavedResFile;
- Handle theResource;
- short theResourceID;
- ResType theResourceType;
- Str255 theResourceName;
- short i, j;
- short theNumberOfTypes, theNumberOfResources;
-
- if (aSourceResFile != 0) { /* If there IS one! */
- theSavedResFile = CurResFile();
- UseResFile(aSourceResFile);
- theNumberOfTypes = Count1Types();
- for (i = 1; i <= theNumberOfTypes; i++) {
- Get1IndType(&theResourceType, i);
- if (!InTypeList(theResourceType, aTypeCount, aTypeList)) {
- theNumberOfResources =
- Count1Resources(theResourceType);
- for (j = 1; j <= theNumberOfResources; j++) {
- theResource = Get1IndResource(theResourceType, j);
- if (theResource != nil) {
- GetResInfo(theResource, &theResourceID,
- &theResourceType, theResourceName);
- DetachResource(theResource);
- UseResFile(aDestinationResFile);
- AddResource(theResource, theResourceType,
- theResourceID, theResourceName);
- UseResFile(aSourceResFile);
- }
- }
- }
- }
- UseResFile(theSavedResFile);
- }
- return theError;
- }
-
- OSErr DSGetFileSize(unsigned long *aFileSize) {
- /*
- * This should be a DSDocumentList routine which calls back to the
- * document.
- */
- OSErr theError = noErr;
-
- *aFileSize = 0;
- return theError;
- }
-
- OSErr GetTrashSpace(short aVRefNum, unsigned long *aTrashSpace) {
- OSErr theError = noErr;
- FSSpec theTrash;
-
- theError = GetTrashFolder(aVRefNum, &theTrash);
- if (theError == noErr) {
- FSGetFolderSize(&theTrash, aTrashSpace);
- } else {
- *aTrashSpace = 0;
- }
- return theError;
- }
-
- OSErr GetTemporaryName(Str255 theTemporaryName) {
- OSErr theError = noErr;
- unsigned long theTime;
- Str255 theName;
-
- GetDateTime(&theTime);
- NumToString(theTime, theName);
- BlockMoveData(theName, theTemporaryName, (theName[0] + 1));
- return theError;
- }
-
- Boolean InTypeList(ResType aType, short aTypeCount, ResType *aList) {
- Boolean inTypeList;
- short i;
-
- inTypeList = false;
- for (i=0; i<aTypeCount; i++) {
- if (aType == aList[i]) {
- inTypeList = true;
- break;
- }
- }
- return inTypeList;
- }
-
-